﻿Imports Microsoft.Win32
Imports System.Diagnostics
Imports System.ComponentModel

' Process Controller for openvpn.exe (Version 2.3)
' (c) 2012 chris burri @ burri IT systems

Public Class OpenVPNCntrl

#Region "Declarations"
    ' some constants
    Private Const OpenVPN_Bin_Name = "openvpn.exe"
    Private Const OpenVPN_Exit_Event_Prefix = "BITS.OVPN_SIGTERM"
    Private Const OpenVPN_Shutdown_Timeout = 5000
    Private Const OpenVPN_Kill_Wait = 1000
    Private Const OpenVPN_Default_Priority As System.Diagnostics.ProcessPriorityClass = ProcessPriorityClass.AboveNormal
    Private Const Watchdog_Interval = 1000
    Private Const Watchdog_Restart_Wait = 1000
    Private Const Queue_Proc_Interval = 50

    ' openVPN log message triggers
    Private Const Log_TCP_Connecting = "Attempting to establish TCP connection with [AF_INET]"
    Private Const Log_TCP_Connected = "TCP connection established with [AF_INET]"
    Private Const Log_TCP_Closing = "TCP/UDP: Closing socket"
    Private Const Log_VPN_Fatal_Error = "Exiting due to fatal error"
    Private Const Log_VPN_Push_Timeout = "No reply from server after sending"
    Private Const Log_VPN_Initalized = "Initialization Sequence Completed"
    Private Const Log_VPN_RouteFailed = "ERROR: Windows route add command failed"
    Private Const Log_VPN_TAP_Configured = "Notified TAP-Win32 driver to set a DHCP IP/netmask of"
    Private Const Log_VPN_TAP_InUse = "All TAP-Win32 adapters on this system are currently in use."
    Private Const Log_VPN_TAP_Fail = "CreateFile failed on TAP device:"
    Private Const Log_VPN_Cant_Resolve = "RESOLVE: Cannot resolve host address:"
    Private Const Log_VPN_SIGTERM_1 = "SIGTERM["
    Private Const Log_VPN_SIGTERM_2 = "] received, process exiting"
    Private Const Log_VPN_TAP_Closing = "Closing TUN/TAP interface"
    Private Const Log_VPN_Restart = "process restarting"
    Private Const Log_VPN_TLS_Handshake_Begin = "TLS: Initial packet from"
    Private Const Log_VPN_TLS_Handshake_Complete = "Peer Connection Initiated with"
    Private Const Log_VPN_Options_Error = "Options error:"
    Private Const Log_VPN_Startup1 = "OpenVPN 2."
    Private Const Log_VPN_Startup2 = "Win32-MSVC++"
    Private Const Log_VPN_TAP_NotFound1 = "TAP-Win32 adapter'"
    Private Const Log_VPN_TAP_NotFound2 = "'not found"
    Private Const Log_VPN_UDP_Listening = "UDPv4 link local (bound): "
    Private Const Log_VPN_TCP_Listening = "Listening for incoming TCP connection on"




    Private _VPN_Bin_Path As String = ""
    Private _VPN_SIGTERM_Instance As String = ""
    Private _Lock_Output As New Object
    Private _Lock_Error As New Object
    Private _VPN_RouteFail As Boolean = False
    Private _VPN_TUNTAPCfg As Boolean = False
    Private _VPN_Initialized As Boolean = False
    Private _VPN_Shutdown As Boolean = False
    Private _VPN_Starting As Boolean = False
    Private _VPN_Stopping As Boolean = False

    ' internal objects
    Private _EventQueue As New Queue
    Private WithEvents _VPN_Process As Process
    'Private WithEvents Watchdog As Timers.Timer
    Private WithEvents QueueProc As Timers.Timer

    ' OpenVPN SIGTERM/Exit Event
    Private _VPN_Exit_Event As System.Threading.EventWaitHandle
#End Region

#Region "Public Interface"
    Public Event OutputReceived(OutputLine As String) ' raw stdout data
    Public Event ErrorReceived(ErrorLine As String)   ' raw stderr data

    Public Event VPN_Restart_Watchdog()                                             ' Watchdog restarting VPN
    Public Event VPN_Config_Error(ErrorMessage As String)                           ' Error in configuration
    Public Event VPN_TCP_Connecting(RemoteHost As String, RemotePort As Integer)    ' TCP connecting
    Public Event VPN_TCP_Connected(RemoteHost As String, RemotePort As Integer)     ' TCP connected
    Public Event VPN_TCP_Listening(LocalHost As String, LocalPort As Integer)       ' TCP listening
    Public Event VPN_UDP_Listening(LocalHost As String, LocalPort As Integer)       ' UDP listening
    Public Event VPN_TCPUDP_Closing()                                               ' TCP/UDP closing
    Public Event VPN_DNS_Resolve_Failed(Hostname As String)                         ' failed to resolve remote DNS name
    Public Event VPN_TLS_Handshake_Begin(PeerIP As String, PeerPort As Integer)     ' TLS handshake begun
    Public Event VPN_TLS_Handshake_Complete(PeerName As String)                     ' TLS handshake completed
    Public Event VPN_TAP_Unavailable()                                              ' No TAP/TUN adapter available
    Public Event VPN_PUSH_Timeout()                                                 ' PUSH request timeout occured
    Public Event VPN_DHCP_Configured(LocalIP As String, LocalNetmask As String, InterfaceGUID As Guid, DHCPServerIP As String, LeaseTime As UInt32) ' TUN/TAP Configured
    Public Event VPN_Up(AdapterConfigured As Boolean, RoutesAdded As Boolean)       ' VPN link established
    Public Event VPN_Down()                                                         ' VPN link down
    Public Event VPN_Start()                                                        ' VPN starting
    Public Event VPN_Restart()                                                      ' VPN restarting
    Public Event VPN_Stop()                                                         ' VPN stopped

    Public Property Configuration As New List(Of String)                            ' Configuration list
    Public Property BinPath As String                                               ' Path to openvpn executable
    Public Property BinName As String                                               ' name of openvpn executable
    Public Property BinPriority As System.Diagnostics.ProcessPriorityClass = OpenVPN_Default_Priority   ' default priority for openvpn.exe process

    Public ReadOnly Property Running As Boolean                                     ' true if VPN process is running
        Get
            Try
                If Not IsNothing(_VPN_Process) Then
                    Return Not _VPN_Process.HasExited
                Else
                    Return False
                End If
            Catch ex As InvalidOperationException
                ' ignore invalid operation exception
            Catch ex As Exception

                Dbg("Running(): " & ex.Message)
                Return False
            End Try
        End Get
    End Property

    Public ReadOnly Property PID As Integer
        Get
            Try
                If Not IsNothing(_VPN_Process) Then
                    Return _VPN_Process.Id
                Else
                    Return 0
                End If
            Catch ex As Exception
                Dbg(".PID: " & ex.Message)
                Return 0
            End Try
        End Get
    End Property

    Public Sub Start()                                                              ' start openvpn process
        Call StartVPN()
    End Sub

    Public Sub [Stop]()                                                             ' stop openvpn process
        Call StopVPN()
    End Sub

    Public Function GetOVPNVersion() As String
        Dim VInfo As String = ""
        Dim VEnd As Integer = 0
        _VPN_Bin_Path = System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)
        If _VPN_Bin_Path = "" Then _VPN_Bin_Path = System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
        _VPN_Bin_Path = _VPN_Bin_Path & "\openvpn\bin\"
        If _BinPath = "" Then _BinPath = _VPN_Bin_Path
        If Not _BinPath.EndsWith("\") Then _BinPath = _BinPath & "\"
        If _BinName = "" Then _BinName = OpenVPN_Bin_Name
        ' set up SIGTERM/Exit Event for this particular openvpn.exe instance
        _VPN_SIGTERM_Instance = OpenVPN_Exit_Event_Prefix & "." & Guid.NewGuid.ToString
        _VPN_Exit_Event = New System.Threading.EventWaitHandle(False, Threading.EventResetMode.ManualReset, _VPN_SIGTERM_Instance)
        ' set up openvpn.exe process
        _VPN_Process = New Process()
        With _VPN_Process
            ' setup working dir, exe path and base priority
            .StartInfo.WorkingDirectory = _BinPath
            .StartInfo.FileName = _BinPath & _BinName
            ' get openvpn.exe version
            .StartInfo.Arguments = "--version"
            ' don't use shellexecute
            .StartInfo.UseShellExecute = False
            ' dont show window nor error popup
            .StartInfo.CreateNoWindow = True
            .StartInfo.ErrorDialog = False
            ' setup event queue
            _EventQueue = New Queue
            QueueProc = New Timers.Timer(Queue_Proc_Interval)
            QueueProc.Start()
            ' redirect stdout and stderr, enable exit event
            .StartInfo.RedirectStandardOutput = True
            .StartInfo.RedirectStandardError = True
            .Start()
            VInfo = .StandardOutput.ReadToEnd()
            .Close()
        End With
        VInfo = Microsoft.VisualBasic.Left(VInfo, 30)
        VEnd = InStr(VInfo, "Win32")
        Return Microsoft.VisualBasic.Left(VInfo, VEnd - 1)
    End Function
#End Region

#Region "Process Handling"
    Private Sub StartVPN()
        If _VPN_Starting Then Exit Sub
        _VPN_Starting = True
        Try
            'Trace.WriteLine("Starting openVPN...")
            _VPN_Shutdown = False
            ' set up path
            _VPN_Bin_Path = System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)
            If _VPN_Bin_Path = "" Then _VPN_Bin_Path = System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
            _VPN_Bin_Path = _VPN_Bin_Path & "\openvpn\bin\"
            If _BinPath = "" Then _BinPath = _VPN_Bin_Path
            If Not _BinPath.EndsWith("\") Then _BinPath = _BinPath & "\"
            If _BinName = "" Then _BinName = OpenVPN_Bin_Name
            ' set up SIGTERM/Exit Event for this particular openvpn.exe instance
            _VPN_SIGTERM_Instance = OpenVPN_Exit_Event_Prefix & "." & Guid.NewGuid.ToString
            _VPN_Exit_Event = New System.Threading.EventWaitHandle(False, Threading.EventResetMode.ManualReset, _VPN_SIGTERM_Instance)
            ' set up openvpn.exe process
            _VPN_Process = New Process()
            With _VPN_Process
                ' setup working dir, exe path and base priority
                .StartInfo.WorkingDirectory = _BinPath
                .StartInfo.FileName = _BinPath & _BinName
                ' add verbosity 4
                ' ### TODO: override existing verb
                _Configuration.Add("verb 4")
                ' add service parameter for SIGTERM event to configuration
                ' ## TODO: disable when "service" directive contained in config
                _Configuration.Add("service " & _VPN_SIGTERM_Instance & " " & "0")
                ' set up process command line arguments
                .StartInfo.Arguments = BuildCmdLineArgs(_Configuration)
                ' don't use shellexecute
                .StartInfo.UseShellExecute = False
                ' dont show window nor error popup
                .StartInfo.CreateNoWindow = True
                .StartInfo.ErrorDialog = False
                ' setup event queue
                _EventQueue = New Queue
                QueueProc = New Timers.Timer(Queue_Proc_Interval)
                QueueProc.Start()
                ' redirect stdout and stderr, enable exit event
                .StartInfo.RedirectStandardOutput = True
                .StartInfo.RedirectStandardError = True
                .EnableRaisingEvents = True
            End With
            ' reset tuntap/route error flags
            _VPN_TUNTAPCfg = False
            _VPN_RouteFail = False
            ' start openvpn.exe process
            _VPN_Process.Start()
            ' set priority class
            _VPN_Process.PriorityClass = _BinPriority
            ' start output redirection
            _VPN_Process.BeginErrorReadLine()
            _VPN_Process.BeginOutputReadLine()
        Catch ex As Exception
            Trace.WriteLine("Start(): " & ex.Message)
        End Try
    End Sub

    Private Sub StopVPN(Optional ForceKill As Boolean = False)
        If _VPN_Stopping Then Exit Sub
        _VPN_Stopping = True
        Try
            'Trace.WriteLine("Stopping openVPN...")
            _VPN_Shutdown = True
            If Not IsNothing(_VPN_Process) Then
                If Not ForceKill Then
                    ' signal exit event to openvpn
                    _VPN_Exit_Event.Set()
                    ' wait for openvpn to shutdown
                    _VPN_Process.WaitForExit(OpenVPN_Shutdown_Timeout)
                    ' still running? forcibly kill process
                    If Not _VPN_Process.HasExited Then
                        Dbg("SIGTERM failed. Killing process...")
                        _VPN_Process.Kill()
                    End If
                Else
                    ' forcibly kill process
                    _VPN_Process.Kill()
                    ' sleep for OpenVPN_Kill_Wait milliseconds (defaults to 1000)
                    System.Threading.Thread.Sleep(OpenVPN_Kill_Wait)
                End If
                RaiseEvent VPN_Stop()
            End If
        Catch ex As InvalidOperationException
            ' ignore invalid operation exception
        Catch ex As Exception
            Trace.WriteLine("Stop(): " & ex.Message)
        End Try
    End Sub
#End Region

#Region "Output Redirection and Process Event Handlers"
    Private Sub _VPN_Process_ErrorDataReceived(sender As Object, e As System.Diagnostics.DataReceivedEventArgs) Handles _VPN_Process.ErrorDataReceived
        Dim proc As Process = CType(sender, Process)
        Dim ProcID As Integer
        Try
            ProcID = proc.Id
        Catch ex As Exception
            ProcID = 0
        End Try
        Try
            SyncLock _Lock_Error
                Dim newmsg As New QueuedMessage
                'Trace.WriteLine("Proc=" & proc.ProcessName & " (" & proc.Id & ") ! " & outLine.Data)
                newmsg.Type = QueuedMessageTypes.Error
                newmsg.PID = ProcID
                newmsg.Message = e.Data
                Enqueue(newmsg)
            End SyncLock
        Catch ex As Exception
            Trace.WriteLine("OpenVPNController:OnReadErrOutput(): " & ex.Message)
        End Try
    End Sub

    Private Sub _VPN_Process_OutputDataReceived(sender As Object, e As System.Diagnostics.DataReceivedEventArgs) Handles _VPN_Process.OutputDataReceived
        Dim proc As Process = CType(sender, Process)
        Dim ProcID As Integer
        Try
            ProcID = proc.Id
        Catch ex As Exception
            ProcID = 0
        End Try
        Try
            SyncLock _Lock_Output
                Dim newmsg As New QueuedMessage
                'Trace.WriteLine("Proc=" & proc.ProcessName & " (" & proc.Id & ") > " & outLine.Data)
                newmsg.Type = QueuedMessageTypes.Output
                newmsg.PID = ProcID
                newmsg.Message = e.Data
                Enqueue(newmsg)
            End SyncLock
        Catch ex As Exception
            Trace.WriteLine("OnReadStdOutput: " & ex.Message)
        End Try
    End Sub

    Private Sub _VPN_Process_Exited(sender As Object, e As System.EventArgs) Handles _VPN_Process.Exited
        Dim proc As Process = CType(sender, Process)
        Dim ProcID As Integer
        Dim newmsg As New QueuedMessage
        Try
            ProcID = proc.Id
        Catch ex As Exception
            ProcID = 0
        End Try
        Try
            ' dispose exit Win32 Event
            _VPN_Exit_Event.Dispose()
            newmsg.PID = ProcID
            newmsg.Message = ""
            If _VPN_Shutdown Then
                ' process shutdown
                ' queue exit event message
                newmsg.Type = QueuedMessageTypes.Exit
                Enqueue(newmsg)
            Else
                _VPN_Process.CancelOutputRead()
                _VPN_Process.CancelErrorRead()
                ' watchdog triggered
                newmsg.Type = QueuedMessageTypes.Watchdog
                Enqueue(newmsg)
            End If
        Catch ex As Exception
            Trace.WriteLine("_VPN_Process_Exited(): " & ex.Message)
        End Try
    End Sub
#End Region

#Region "Event Queue Processing"
    Private Enum QueuedMessageTypes
        Output = 0
        [Error] = 1
        [Exit]
        Watchdog
    End Enum

    Private Class QueuedMessage
        Public Type As QueuedMessageTypes
        Public PID As Long
        Public Message As String
    End Class

    Private Sub Enqueue(Message As QueuedMessage)
        Dim Q As Queue = Queue.Synchronized(_EventQueue)
        Q.Enqueue(Message)
        QueueProc.Start()
    End Sub


    Private Sub QueueProc_Elapsed(sender As Object, e As System.Timers.ElapsedEventArgs) Handles QueueProc.Elapsed
        Try
            QueueProc.Stop()
            ProcessQueue()
        Catch ex As Exception
            Trace.WriteLine("QueueProc_Elapsed(): " & ex.Message)
        End Try
    End Sub

    Private Sub ProcessQueue()
        Try
            Dim Q As Queue = Queue.Synchronized(_EventQueue)
            Dim msg As QueuedMessage
            While Q.Count > 0
                msg = CType(Q.Dequeue, QueuedMessage)
                Select Case msg.Type
                    Case QueuedMessageTypes.Error
                        RaiseEvent ErrorReceived(msg.Message)
                    Case QueuedMessageTypes.Output
                        RaiseEvent OutputReceived(msg.Message)
                        ProcessLog(msg.Message)
                    Case QueuedMessageTypes.Exit
                        ' close process
                        _VPN_Process.Close()
                        _VPN_Process.Dispose()
                        ' VPN has stopped
                        _VPN_Stopping = False
                        ' raise stop event
                        RaiseEvent VPN_Stop()
                    Case QueuedMessageTypes.Watchdog
                        If Not _VPN_Shutdown Then
                            ' raise watchdog triggered event
                            RaiseEvent VPN_Restart_Watchdog()
                            ' grace period
                            System.Threading.Thread.Sleep(Watchdog_Restart_Wait)
                            ' restart
                            Call StartVPN()
                        Else
                            ' don't restart, just exit
                            RaiseEvent VPN_Stop()
                        End If
                End Select
            End While
        Catch ex As Exception
            Trace.WriteLine("ProcessQueue(): " & ex.Message)
        End Try
    End Sub

#End Region

#Region "OpenVPN Log Processing"
    Dim Elements() As String
    Dim RemoteSock As String = ""
    Dim RemoteSockPos As Integer = 0
    Dim RemoteHost As String = ""
    Dim RemotePort As Integer = 0
    Dim LocalHost As String = ""
    Dim LocalPort As Integer = 0

    Public Sub ProcessLog(Message As String)
        If Not IsNothing(Message) Then

            'Dbg("ProcessLog(): " & Message)

            If Message.Contains(Log_VPN_Initalized) Then
                _VPN_Initialized = True
                ' completed without errors?
                RaiseEvent VPN_Up(_VPN_TUNTAPCfg, Not _VPN_RouteFail)
                Exit Sub
            End If

            If Message.Contains(Log_VPN_UDP_Listening) Then
                _VPN_Initialized = True
                RemoteSockPos = InStr(Message, "INET]") + 5
                RemoteSock = Mid(Message, RemoteSockPos)
                Elements = Split(RemoteSock, ":")
                LocalHost = Elements(0)
                LocalPort = Integer.Parse(Elements(1))
                RaiseEvent VPN_UDP_Listening(LocalHost, LocalPort)
            End If

            If Message.Contains(Log_VPN_TCP_Listening) Then
                _VPN_Initialized = True
                RemoteSockPos = InStr(Message, "INET]") + 5
                RemoteSock = Mid(Message, RemoteSockPos)
                Elements = Split(RemoteSock, ":")
                LocalHost = Elements(0)
                LocalPort = Integer.Parse(Elements(1))
                RaiseEvent VPN_TCP_Listening(LocalHost, LocalPort)
            End If

            If Message.Contains(Log_TCP_Connecting) Then
                ' connecting tcp 
                RemoteSockPos = InStr(Message, "INET]") + 5
                RemoteSock = Mid(Message, RemoteSockPos)
                Elements = Split(RemoteSock, ":")
                RemoteHost = Elements(0)
                RemotePort = Integer.Parse(Elements(1))
                RaiseEvent VPN_TCP_Connecting(RemoteHost, RemotePort)
                Exit Sub
            End If

            If Message.Contains(Log_TCP_Connected) Then
                ' tcp connected
                RemoteSockPos = InStr(Message, "INET]") + 5
                RemoteSock = Mid(Message, RemoteSockPos)
                Elements = Split(RemoteSock, ":")
                RemoteHost = Elements(0)
                RemotePort = Integer.Parse(Elements(1))
                RaiseEvent VPN_TCP_Connected(RemoteHost, RemotePort)
                Exit Sub
            End If


            If Message.Contains(Log_VPN_RouteFailed) Then
                ' failed to add route
                _VPN_RouteFail = True
                Exit Sub
            End If

            If Message.Contains(Log_TCP_Closing) Then
                RaiseEvent VPN_TCPUDP_Closing()
                RaiseVPNDownEvent()
                Exit Sub
            End If

            If Message.Contains(Log_VPN_TAP_Closing) Then
                RaiseVPNDownEvent()
                Exit Sub
            End If

            If Message.Contains(Log_VPN_Restart) Then
                ' timeout
                RaiseEvent VPN_Restart()
                RaiseVPNDownEvent()
                Exit Sub
            End If

            If Message.Contains(Log_VPN_Cant_Resolve) Then
                ' can't resolve host name
                ' ignored
                Exit Sub
            End If

            If Message.Contains(Log_VPN_SIGTERM_1) And Message.Contains(Log_VPN_SIGTERM_2) Then
                ' process shutdown
                RaiseVPNDownEvent()
                Exit Sub
            End If

            If Message.Contains(Log_VPN_Push_Timeout) Then
                ' PUSH request(s) timed out
                RaiseEvent VPN_PUSH_Timeout()
                Exit Sub
            End If

            If Message.Contains(Log_VPN_Startup1) And Message.Contains(Log_VPN_Startup2) Then
                ' openvpn.exe initialized. 
                _VPN_Starting = False
                ' Raise start event
                RaiseEvent VPN_Start()
                Exit Sub
            End If

            If Message.Contains(Log_VPN_TLS_Handshake_Begin) Then
                Dim SockPos As Integer = InStr(Message, "INET]") + 4
                Dim SockStr As String = Mid(Message, SockPos + 1, InStr(Message, ",") - SockPos - 1)
                Dim Sock() As String = Split(SockStr, ":")
                Dim TLSRemIP As String = Sock(0)
                Dim TLSRemPort As Integer = Integer.Parse(Sock(1))
                RaiseEvent VPN_TLS_Handshake_Begin(TLSRemIP, TLSRemPort)
                Exit Sub
            End If

            If Message.Contains(Log_VPN_TLS_Handshake_Complete) Then
                Dim NameStart As Integer = InStr(Message, "[") + 1
                Dim NameEnd As Integer = InStr(Message, "]")
                Dim PeerName = Mid(Message, NameStart, NameEnd - NameStart)
                PeerName = PeerName.Replace("[", "")
                RaiseEvent VPN_TLS_Handshake_Complete(PeerName)
                Exit Sub
            End If

            If Message.Contains(Log_VPN_TAP_InUse) Or Message.Contains(Log_VPN_TAP_Fail) Or _
                    (Message.Contains(Log_VPN_TAP_NotFound1) And Message.Contains(Log_VPN_TAP_NotFound2)) Then
                RaiseEvent VPN_TAP_Unavailable()
                RaiseEvent VPN_TCPUDP_Closing()
                Exit Sub
            End If

            If Message.Contains(Log_VPN_Fatal_Error) Then
                RaiseVPNDownEvent()
                Exit Sub
            End If

            If Message.Contains(Log_VPN_Options_Error) Then
                Dim OptErr As String = Mid(Message, InStr(Message, Log_VPN_Options_Error, CompareMethod.Binary) + Len(Log_VPN_Options_Error) + 1)
                RaiseEvent VPN_Config_Error(OptErr)
                Exit Sub
            End If

            If Message.Contains(Log_VPN_TAP_Configured) Then
                ' tunnel ip set
                _VPN_TUNTAPCfg = True
                Dim Info As String = Mid(Message, Len(Log_VPN_TAP_Configured) + 1)
                Dim OnInterface As Integer = InStr(Info, " on interface ")
                Dim OfPos As Integer = InStr(Info, "of ")
                Dim IPMask As String = Mid(Info, OfPos + 3, OnInterface - OfPos - 3)
                Dim El() As String = Split(IPMask, "/")
                Dim LocalIP As String = El(0)
                Dim LocalNetmask As String = El(1)
                Dim GuidStartPos As Integer = InStr(Info, " {") + 1
                Dim GuidEndPos As Integer = InStr(Info, "} ") + 1
                Dim GuidString As String = Mid(Info, GuidStartPos, GuidEndPos - GuidStartPos)
                Dim InterfaceGuid As New Guid(GuidString)
                Dim DHCPPos As Integer = InStr(Info, "serv: ") + 6
                Dim DHCPEnd As Integer = InStr(Info, ", lease")
                Dim DHCPStr As String = Mid(Info, DHCPPos, DHCPEnd - DHCPPos)
                Dim DHCPServerIP As String = DHCPStr
                Dim LeasePos As Integer = InStr(Info, "time: ") + 5
                Dim LeaseStr As String = Mid(Info, LeasePos)
                LeaseStr = LeaseStr.Replace("]", "")
                Dim LeaseTime As UInt32 = UInt32.Parse(LeaseStr)
                RaiseEvent VPN_DHCP_Configured(LocalIP, LocalNetmask, InterfaceGuid, DHCPServerIP, LeaseTime)
                Exit Sub
            End If
        End If
    End Sub
#End Region

#Region "Config Directive Processing"
    Private Sub ProcessConfig(ConfigLine As String)
        If Not ConfigLine = "" Then
            Dim firstSpace As Integer = InStr(ConfigLine, " ")
            Dim Directive As String = Left(ConfigLine, firstSpace - 1)
            Dim Parameters As String = Mid(ConfigLine, firstSpace + 1)
            Select Case LCase(Directive)
                Case "#svc-priority"
                    Select Case LCase(Parameters)
                        Case "-2", "low", "idle"
                            _BinPriority = ProcessPriorityClass.Idle
                        Case "-1", "belownormal"
                            _BinPriority = ProcessPriorityClass.BelowNormal
                        Case "0", "normal"
                            _BinPriority = ProcessPriorityClass.Normal
                        Case "1", "abovenormal"
                            _BinPriority = ProcessPriorityClass.AboveNormal
                        Case "2", "high"
                            _BinPriority = ProcessPriorityClass.High
                        Case "3", "realtime"
                            _BinPriority = ProcessPriorityClass.RealTime
                    End Select
                Case "#svc.disabled"

            End Select
        End If
    End Sub
#End Region

#Region "Helper Functions"
    Private Sub RaiseVPNDownEvent()
        If _VPN_Initialized Then
            _VPN_Initialized = False
            RaiseEvent VPN_Down()
        End If
    End Sub

    Private Function BuildCmdLineArgs(Configuration As List(Of String)) As String
        Try
            Dim CmdLineArgs As String = ""
            For Each element As String In Configuration
                If Not element = "" Then
                    CmdLineArgs = CmdLineArgs & "--" & element & " "
                End If
            Next
            Return CmdLineArgs
        Catch ex As Exception
            Trace.WriteLine("BuildCmdLineArgs(): " & ex.Message)
        End Try
    End Function

    Private Sub Log(Message As String, EventType As EventLogEntryType)
        OpenVPNLogging.WLog("OpenVPNController: " & Message, EventType)
    End Sub
    Private Sub Dbg(Message As String)
        OpenVPNLogging.WDbg("OpenVPNController: " & Message)
    End Sub

#End Region

End Class

